﻿using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using KinonekoSoftware.ArkSpark.Core;
using KinonekoSoftware.ArkSpark.Runtimes;
using KinonekoSoftware.ArkSpark.Runtimes.Presenters;
using KinonekoSoftware.Foundation.UI.Controls;
using KinonekoSoftware.Foundation.UI.Controls.Selectors;
using Microsoft.Xaml.Behaviors;

namespace KinonekoSoftware.ArkSpark.Controls
{
    public class DocumentEditor : ForestListBox
    {
        public static readonly Point Zero = new Point();
        public static readonly DependencyProperty EditingResourcesProperty =
                DependencyProperty.Register(
                                            nameof(EditingResources),
                                            typeof(ResourceDictionary),
                                            typeof(DocumentEditor),
                                            new PropertyMetadata(new ResourceDictionary()));


        public static readonly DependencyProperty MessageBusProperty =
                DependencyProperty.Register(
                                            nameof(MessageBus),
                                            typeof(IArkSparkMQ),
                                            typeof(DocumentEditor),
                                            new PropertyMetadata(default(IArkSparkMQ), OnMessageBusChanged));

        private static void OnMessageBusChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var editor = (DocumentEditor)sender;

            if (e.OldValue is IArkSparkMQ old)
            {
                //
                // 清空引用
                old.ForwardHandler = null;
            }

            if (editor is null || e.NewValue is not IArkSparkMQ bus)
            {
                return;
            }

            //
            //
            bus.ForwardHandler = editor.OnEventHandler;
        }

        //-------------------------------------------------------------
        //
        //          ParagraphElement
        //
        //-------------------------------------------------------------
        private          ScrollViewer                   _scrollViewer;
        private readonly List<FrameworkElement>         _visibleCollection        = new List<FrameworkElement>();
        private readonly List<DocumentElementPresenter> _visibleElementCollection = new List<DocumentElementPresenter>();

        //-------------------------------------------------------------
        //
        //          ParagraphElement
        //
        //-------------------------------------------------------------
        public DocumentEditor()
        {
            VisibleControls = new ReadOnlyCollection<FrameworkElement>(_visibleCollection);
            VisibleElements = new ReadOnlyCollection<DocumentElementPresenter>(_visibleElementCollection);
        }

        //-------------------------------------------------------------
        //
        //          ParagraphElement
        //
        //-------------------------------------------------------------

        private void OnEventHandler(IArkSparkEvent @event)
        {
            if (@event is not NewSelectionEventArgs args)
            {
                return;
            }

            //
            //
            SelectedItem = args.EditingItem;
        }

        //-------------------------------------------------------------
        //
        //          ParagraphElement
        //
        //-------------------------------------------------------------

        private void OnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            var children = ThisItemsPanel?.Children;

            //
            //
            if (children is null)
            {
                return;
            }

            //
            //
            _visibleCollection.Clear();
            _visibleElementCollection.Clear();
            var vHeight = ThisScrollViewer.ViewportHeight;


            //
            //
            foreach (FrameworkElement child in children)
            {
                var point = child.TranslatePoint(Zero, this);

                if (point.Y + child.ActualHeight >= 0 && point.Y < vHeight)
                {
                    _visibleCollection.Add(child);

                    if (child.DataContext is DocumentElementPresenter element)
                    {
                        _visibleElementCollection.Add(element);
                    }
                }
            }

            MessageBus?.OnNext(new NewVisibleScopedEventArgs(_visibleElementCollection));
        }

        
        //-------------------------------------------------------------
        //
        //          ParagraphElement
        //
        //-------------------------------------------------------------

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            //
            //
            _scrollViewer = Xaml.FindVisualChild<ScrollViewer>(this).FirstOrDefault();

            //
            //
            ThisScrollViewer.ScrollChanged += OnScrollChanged;
        }
        
        
        protected sealed override DependencyObject GetContainerForItemOverride()
        {
            return new DocumentEditorItem();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is DocumentEditorItem;
        }

        //-------------------------------------------------------------
        //
        //          ParagraphElement
        //
        //-------------------------------------------------------------
        public             IReadOnlyList<FrameworkElement>         VisibleControls { get; }
        public             IReadOnlyList<DocumentElementPresenter> VisibleElements { get; }
        protected internal VirtualizingStackPanel                  ThisItemsPanel  { get; internal set; }

        protected internal ScrollViewer ThisScrollViewer => _scrollViewer;

        public ResourceDictionary EditingResources
        {
            get
            {
                var val = (ResourceDictionary)GetValue(EditingResourcesProperty);

                if (val is null)
                {
                    val = new ResourceDictionary();
                    SetValue(EditingResourcesProperty, val);
                }

                return val;
            }
            set => SetValue(EditingResourcesProperty, value);
        }

        public IArkSparkMQ MessageBus
        {
            get => (IArkSparkMQ)GetValue(MessageBusProperty);
            set => SetValue(MessageBusProperty, value);
        }
    }
}